﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Media.Imaging;
using System.Windows;
using System.Diagnostics;

namespace ZX_AAA_Workshop
{
  static class ScreenShotHlp
  {
    public static Bitmap CaptureScreen()
    {
      return CaptureWindow(User32.GetDesktopWindow());
    }

    public static Bitmap CaptureWindow(IntPtr handle)
    {
      //var control = Control.FromHandle(handle);
      //var bitmap = new Bitmap(control.Width, control.Height);
      //control.DrawToBitmap(bitmap, Rectangle.FromLTRB(0, 0, control.Width, control.Height));
      //return bitmap;

      var windowRect = GetWindowRect(handle);
      return CaptureWindow(handle, new Int32Rect(0, 0, windowRect.Width, windowRect.Height));
    }

    public static Bitmap CaptureWindow(IntPtr handle, Int32Rect rect)
    {
      IntPtr windowHdc = User32.GetWindowDC(handle);
      try
      {
        IntPtr bitmapHdc = Gdi32.CreateCompatibleDC(windowHdc);
        try
        {
          IntPtr hBitmap = Gdi32.CreateCompatibleBitmap(windowHdc, rect.Width, rect.Height);
          try
          {
            IntPtr hOld = Gdi32.SelectObject(bitmapHdc, hBitmap);
            try
            {
              Gdi32.BitBlt(bitmapHdc, 0, 0, rect.Width, rect.Height, windowHdc, rect.X, rect.Y, Gdi32.SRCCOPY);
            }
            finally
            {
              Gdi32.SelectObject(bitmapHdc, hOld);
            }
            Bitmap image = Bitmap.FromHbitmap(hBitmap);
            return image;
          }
          finally
          {
            Gdi32.DeleteObject(hBitmap);
          }
        }
        finally
        {
          Gdi32.DeleteDC(bitmapHdc);
        }
      }
      finally
      {
        User32.ReleaseDC(handle, windowHdc);
      }

      //IntPtr hdcSrc = User32.GetWindowDC(handle);

      //IntPtr hdcDest = Gdi32.CreateCompatibleDC(hdcSrc);
      //IntPtr hBitmap = Gdi32.CreateCompatibleBitmap(hdcSrc, rect.Width, rect.Height);

      //IntPtr hOld = Gdi32.SelectObject(hdcDest, hBitmap);
      //Gdi32.BitBlt(hdcDest, 0, 0, rect.Width, rect.Height, hdcSrc, rect.X, rect.Y, Gdi32.SRCCOPY);
      //Gdi32.SelectObject(hdcDest, hOld);
      //Gdi32.DeleteDC(hdcDest);
      //User32.ReleaseDC(handle, hdcSrc);

      //Bitmap image = Bitmap.FromHbitmap(hBitmap);
      //Gdi32.DeleteObject(hBitmap);

      //return image;
    }
    // почему-то картинка смещена получается
    //public static Bitmap CaptureWindow(IntPtr handle, Int32Rect rect)
    //{
    //  using (var windowGraphics = Graphics.FromHwnd(handle))
    //  {
    //    var bitmap = new Bitmap(rect.Width, rect.Height, windowGraphics);
    //    using (var bitmapGraphics = Graphics.FromImage(bitmap))
    //    {
    //      var bitmapHdc = bitmapGraphics.GetHdc();
    //      try
    //      {
    //        var windowHdc = windowGraphics.GetHdc();
    //        try
    //        {
    //          Gdi32.BitBlt(bitmapHdc, 0, 0, rect.Width, rect.Height, windowHdc, rect.X, rect.Y, Gdi32.SRCCOPY);
    //        }
    //        finally
    //        {
    //          windowGraphics.ReleaseHdc(windowHdc);
    //        }
    //      }
    //      finally
    //      {
    //        bitmapGraphics.ReleaseHdc(bitmapHdc);
    //      }
    //    }
    //    return bitmap;
    //  }
    //}
    // не работает для Spectaculator-а
    //public static Bitmap CaptureWindow(IntPtr handle, Int32Rect rect)
    //{
    //  var g = Graphics.FromHwnd(handle);
    //  var bmp = new Bitmap(rect.Width, rect.Height, g);
    //  var memoryGraphics = Graphics.FromImage(bmp);
    //  var dc = memoryGraphics.GetHdc();
    //  var success = User32.PrintWindow(handle, dc, 0);
    //  memoryGraphics.ReleaseHdc(dc);
    //  return bmp;
    //}
    public static BitmapSource ToBitmapSource(this System.Drawing.Bitmap source)
    {
      BitmapSource bitSrc = null;

      var hBitmap = source.GetHbitmap();

      try
      {
        bitSrc = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
            hBitmap,
            IntPtr.Zero,
            Int32Rect.Empty,
            BitmapSizeOptions.FromEmptyOptions());
      }
      catch (System.ComponentModel.Win32Exception)
      {
        bitSrc = null;
      }
      finally
      {
        Gdi32.DeleteObject(hBitmap);
      }

      return bitSrc;
    }

    public static Int32Rect GetWindowRect(IntPtr hWnd)
    {
      RECT rect = new RECT();
      User32.GetWindowRect(hWnd, ref rect);
      return new Int32Rect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
    }

    public static Int32Rect GetClientRect(IntPtr hWnd)
    {
      RECT rect;
      User32.GetClientRect(hWnd, out rect);
      POINT leftTopCorner = new POINT();
      User32.ClientToScreen(hWnd, ref leftTopCorner);
      RECT windowRect = new RECT();
      User32.GetWindowRect(hWnd, ref windowRect);
      return new Int32Rect(leftTopCorner.x - windowRect.left, leftTopCorner.y - windowRect.top,
        rect.right - rect.left, rect.bottom - rect.top);
    }

    public static IntPtr[] FindProcessWindows(Process process)
    {
      //return new[] { process.MainWindowHandle };

      var windows = new List<IntPtr>();
      foreach (ProcessThread thread in process.Threads)
      {
        User32.EnumThreadWindows(thread.Id,
          (hWnd, lParam) =>
          {
            windows.Add(hWnd);
            User32.EnumChildWindows(hWnd,
              (hWnd2, lParam2) =>
              {
                windows.Add(hWnd2);
                return true;
              },
              IntPtr.Zero);
            return true;
          },
          IntPtr.Zero);
      }
      return windows.ToArray();

      // возвращает все окна
      //var windows = new List<IntPtr>();
      //User32.EnumWindows(
      //  (hWnd, lParam) =>
      //  {
      //    int processId;
      //    User32.GetWindowThreadProcessId(hWnd, out processId);
      //    if (processId == process.Id)
      //      windows.Add(hWnd);
      //    return true;
      //  },
      //  IntPtr.Zero);
      //return windows.ToArray();
    }

    public static string GetWindowText(IntPtr hWnd)
    {
      int length = User32.GetWindowTextLength(hWnd);
      StringBuilder sb = new StringBuilder(length + 1);
      User32.GetWindowText(hWnd, sb, sb.Capacity);
      return sb.ToString();
    }

    public static IEnumerable<IntPtr> EnumerateWindowAncestorsAndSelf(IntPtr hWnd)
    {
      while (hWnd != IntPtr.Zero)
      {
        yield return hWnd;
        hWnd = User32.GetParent(hWnd);
      }
    }
  }

  static class Gdi32
  {
    [DllImport("gdi32.dll")]
    public static extern bool BitBlt(IntPtr hObject, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hObjectSource, int nXSrc, int nYSrc, int dwRop);
    [DllImport("gdi32.dll")]
    public static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int nWidth, int nHeight);
    [DllImport("gdi32.dll")]
    public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
    [DllImport("gdi32.dll")]
    public static extern bool DeleteDC(IntPtr hDC);
    [DllImport("gdi32.dll")]
    public static extern bool DeleteObject(IntPtr hObject);
    [DllImport("gdi32.dll")]
    public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);

    public const int SRCCOPY = 13369376;
  }

  static class User32
  {
    [DllImport("user32.dll")]
    public static extern IntPtr GetDesktopWindow();
    [DllImport("user32.dll")]
    public static extern IntPtr GetWindowDC(IntPtr hWnd);
    [DllImport("user32.dll")]
    public static extern IntPtr GetWindowRect(IntPtr hWnd, ref RECT rect);
    [DllImport("user32.dll")]
    public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);

    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

    [DllImport("user32.dll")]
    public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
    [DllImport("user32.dll")]
    public static extern bool ClientToScreen(IntPtr hWnd, ref POINT lpPoint);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool EnumThreadWindows(int dwThreadId, EnumWindowsProc lpfn, IntPtr lParam);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);
    
    [DllImport("user32.dll", SetLastError = true)]
    public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);

    [DllImport("User32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern int GetWindowTextLength(IntPtr hWnd);

    [DllImport("user32.dll")]
    public static extern IntPtr WindowFromPoint(POINT Point);

    [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
    public static extern IntPtr GetParent(IntPtr hWnd);
  }

  [StructLayout(LayoutKind.Sequential)]
  struct RECT
  {
    public int left;
    public int top;
    public int right;
    public int bottom;
  }
  [StructLayout(LayoutKind.Sequential)]
  struct POINT
  {
    public int x;
    public int y;
  }

  delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
}
